/*
 * rotate.cpp
 *
 *  Created on: 2019年3月8日
 *      Author: guyadong
 */
#include <type_traits>
#include <stdint.h>
#include <algorithm>
#include <cstring>
#include <vector>
#include "rotate.h"
#include "internal_def.h"

template<typename T,size_t BPP = sizeof(T)>
inline typename std::enable_if<BPP == 1 || BPP == 2 || BPP == 4,void>::type _myswap(void*a, void* b) noexcept{
	std::swap(*((T*)a), *((T*)b));
}
template<typename T, size_t BPP = sizeof(T)>
inline typename std::enable_if<BPP == 3,void>::type _myswap(void*a,void* b) noexcept {
	for (int i = 0; i < BPP; ++i) {
		std::swap(((uint8_t*)a)[i], ((uint8_t*)b)[i]);
	}
}
template<typename T, size_t BPP = sizeof(T)>
inline typename std::enable_if<BPP == 1 || BPP == 2 || BPP == 4,void>::type _mysign(const void*a, void* b) noexcept {
	*((T*)b) = *((T*)a) ;
}
template<typename T, size_t BPP = sizeof(T)>
inline typename std::enable_if<BPP == 3,void>::type _mysign(const void*a, void* b) noexcept {
	for (int i = 0; i < BPP; ++i) {
		((uint8_t*)b)[i] = ((uint8_t*)a)[i];
	}
}
/// 公用缓冲区,用于90,270旋转
static thread_local std::vector<uint8_t> buf(0);

template<typename T, size_t BPP = sizeof(T)>
void rotate90(void*input, int width, int height) noexcept {
	auto size = width*height*BPP;
	buf.resize(size);
	memcpy(buf.data(), input, size);
	uint8_t* src_ptr = buf.data();
	for (int y = 0; y < height; ++y) {
		for (int x = 0; x < width; ++x, src_ptr += BPP) {
			int X = height - 1 - y;
			int Y = x;
			_mysign<T, BPP>(src_ptr, ((uint8_t*)input) + (Y*height + X)*BPP);
		}
	}
}
template<typename T, size_t BPP = sizeof(T)>
void rotate180(void*input, int width, int height) noexcept {
	int stride = width*BPP;
	uint8_t* srcline = (uint8_t*)input;
	uint8_t* dstline = (uint8_t*)input + (height - 1)*stride;
	for (int y = 0; y < ((height + 1) >> 1); ++y) {
		int dx = stride - BPP;
		for (int sx = 0; sx < stride; sx += BPP, dx -= BPP) {
			_myswap<T, BPP>(srcline + sx, dstline + dx);
		}
		srcline += stride;
		dstline -= stride;
	}
}
template<typename T, size_t BPP = sizeof(T)>
void rotate270(void*input, int width, int height) noexcept {
	auto size = width*height*BPP;
	buf.resize(size);
	memcpy(buf.data(), input, size);
	uint8_t* src_ptr = buf.data();
	for (int y = 0; y < height; ++y) {
		for (int x = 0; x < width; ++x,src_ptr += BPP) {
			int X = y;
			int Y = width - 1 - x;
			_mysign<T, BPP>(src_ptr,((uint8_t*)input) +(Y*height + X)*BPP);
		}
	}
}
static int rotate90(void*input, int width, int height, int bpp) noexcept {
	switch (bpp) {
	case 1:
		rotate90<uint8_t>(input, width, height);
		return JIU_OK;
	case 2:
		rotate90<uint16_t>(input, width, height);
		return JIU_OK;
	case 3:
		rotate90<uint8_t,3>(input, width, height);
		return JIU_OK;
	case 4:
		rotate90<uint32_t>(input, width, height);
		return JIU_OK;
	default:
		return JIU_INVALID_ARG;
	}
}
static int rotate180(void*input, int width, int height, int bpp) noexcept {
	switch (bpp) {
	case 1:
		rotate180<uint8_t>(input, width, height);
		return JIU_OK;
	case 2:
		rotate180<uint16_t>(input, width, height);
		return JIU_OK;
	case 3:
		rotate180<uint8_t,3>(input, width, height);
		return JIU_OK;
	case 4:
		rotate180<uint32_t>(input, width, height);
		return JIU_OK;
	default:
		return JIU_INVALID_ARG;
	}
}

static int rotate270(void*input,int width,int height,int bpp) noexcept {
	switch (bpp) {
	case 1:
		rotate270<uint8_t>(input, width, height);
		return JIU_OK;
	case 2:
		rotate270<uint16_t>(input, width, height);
		return JIU_OK;
	case 3:
		rotate270<uint8_t,3>(input, width, height);
		return JIU_OK;
	case 4:
		rotate270<uint32_t>(input, width, height);
		return JIU_OK;
	default:
		return JIU_INVALID_ARG;
	}
}
template<typename T, size_t BPP = sizeof(T)>
void hflip(void*input, int width, int height) noexcept {
	int stride = width*BPP;
	uint8_t* line = (uint8_t*)input;
	for (int i = 0; i < height; i++)
	{
		for (int j = 0, end_j = (width >> 1)*BPP; j < end_j; j += BPP)
		{
			_myswap<T, BPP>(line + j, line + (stride - BPP) - j);
		}
		line += stride;
	}
}
static void vflip(void*input, int width, int height,int bpp) noexcept {
	auto stride = width*bpp;
	auto srcline = (uint8_t*)input;
	auto dstline = (uint8_t*)input + (height - 1)*stride;
	auto end_line = (uint8_t*)input + (height >> 1)*stride;
	std::vector<uint8_t> tmp(stride);
	for (;srcline < end_line;srcline += stride, dstline -= stride)
	{
		// srcline,dstline行交换
		memcpy(tmp.data(), srcline, stride);
		memcpy(srcline, dstline, stride);
		memcpy(dstline, tmp.data(), stride);
	}
}
int gdface::jimgutil::rotate(void* input,int width,int height,int bpp,int angle) noexcept {
	if (input == nullptr ) {
		return JIU_NULL_PTR;
	}
	if(bpp<1 || bpp >4 ){
		return JIU_INVALID_ARG;
	}
	switch (angle) {
	case 0:
		return JIU_OK;
	case 90:
		return rotate90(input,width,height,bpp);
	case 180:
		return rotate180(input,width,height,bpp);
	case 270:
		return rotate270(input,width,height,bpp);
	default:
		return JIU_INVALID_ARG;
	}
}
int gdface::jimgutil::hflip(void* input,int width,int height,int bpp)noexcept {
	if (input == nullptr ) {
		return JIU_NULL_PTR;
	}
	switch (bpp) {
	case 1:
		::hflip<uint8_t>(input, width, height);
		return JIU_OK;
	case 2:
		::hflip<uint16_t>(input, width, height);
		return JIU_OK;
	case 3:
		::hflip<uint8_t, 3>(input, width, height);
		return JIU_OK;
	case 4:
		::hflip<uint32_t>(input, width, height);
		return JIU_OK;
	default:
		return JIU_INVALID_ARG;
	}
}
int gdface::jimgutil::vflip(void* input,int width,int height,int bpp)noexcept {
	if (input == nullptr ) {
		return JIU_NULL_PTR;
	}
	if (bpp < 1 || bpp > 4) {
		return JIU_INVALID_ARG;
	}
	::vflip(input, width, height, bpp);
	return JIU_OK;
}
